---
title: Popover
package: "@chakra-ui/popover"
image: "components/popover.svg"
description: Popover is a non-modal dialog that floats around a trigger
---

Popover is a non-modal dialog that floats around a trigger. It is used to
display contextual information to the user, and should be paired with a
clickable trigger element.

<ComponentLinks
  theme={{ componentName: "popover" }}
  github={{ package: "popover" }}
  npm={{ package: "@chakra-ui/popover" }}
/>

<carbon-ad></carbon-ad>

`Popover` is built on top of the [Popper.js](https://popper.js.org/) library.

## Import

- `Popover`: The wrapper that provides props, state, and context to its
  children.
- `PopoverTrigger`: Used to wrap the reference (or trigger) element.
- `PopoverContent`: The popover itself.
- `PopoverHeader`: The header of the popover.
- `PopoverBody`: The body of the popover.
- `PopoverArrow`: A visual arrow that points to the reference (or trigger).
- `PopoverCloseButton`: A button to close the popover.

```js
import {
  Popover,
  PopoverTrigger,
  PopoverContent,
  PopoverHeader,
  PopoverBody,
  PopoverFooter,
  PopoverArrow,
  PopoverCloseButton,
} from "@chakra-ui/react"
```

## Basic Usage

When using this component, ensure the children passed to `PopoverTrigger` is
focusable. Users can tab to it using their keyboard, and it can take a `ref`. It
is critical for accessiblity.

> **A11y:** When Popover opens, focus is sent to `PopoverContent`. When it
> closes, focus is returned to the trigger.

```jsx
<Popover>
  <PopoverTrigger>
    <Button>Trigger</Button>
  </PopoverTrigger>
  <PopoverContent>
    <PopoverArrow />
    <PopoverCloseButton />
    <PopoverHeader>Confirmation!</PopoverHeader>
    <PopoverBody>Are you sure you want to have that milkshake?</PopoverBody>
  </PopoverContent>
</Popover>
```

## Rendering the Popover in a Portal

By default, the Popover doesn't render in a Portal. To make them display in a
portal, wrap the `PopoverContent` in a `Portal`

> You might need to **Inspect Element** to see this in action. Notice that
> `PopoverContent` is rendered as a child of `<body>`

```jsx
<Popover>
  <PopoverTrigger>
    <Button>Trigger</Button>
  </PopoverTrigger>
  <Portal>
    <PopoverContent>
      <PopoverArrow />
      <PopoverHeader>Header</PopoverHeader>
      <PopoverCloseButton />
      <PopoverBody>
        <Button colorScheme="blue">Button</Button>
      </PopoverBody>
      <PopoverFooter>This is the footer</PopoverFooter>
    </PopoverContent>
  </Portal>
</Popover>
```

## Focus an element when Popover opens

By default, focus is to sent to `PopoverContent` when it opens. Pass the
`initialFocusRef` prop to send focus to a specific element instead.

```jsx
function WalkthroughPopover() {
  const initialFocusRef = React.useRef()
  return (
    <Popover
      initialFocusRef={initialFocusRef}
      placement="bottom"
      closeOnBlur={false}
    >
      <PopoverTrigger>
        <Button>Trigger</Button>
      </PopoverTrigger>
      <PopoverContent color="white" bg="blue.800" borderColor="blue.800">
        <PopoverHeader pt={4} fontWeight="bold" border="0">
          Manage Your Channels
        </PopoverHeader>
        <PopoverArrow />
        <PopoverCloseButton />
        <PopoverBody>
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
          eiusmod tempor incididunt ut labore et dolore.
        </PopoverBody>
        <PopoverFooter
          border="0"
          d="flex"
          alignItems="center"
          justifyContent="space-between"
          pb={4}
        >
          <Box fontSize="sm">Step 2 of 4</Box>
          <ButtonGroup size="sm">
            <Button colorScheme="green">Setup Email</Button>
            <Button colorScheme="blue" ref={initialFocusRef}>
              Next
            </Button>
          </ButtonGroup>
        </PopoverFooter>
      </PopoverContent>
    </Popover>
  )
}
```

## Trapping Focus within Popover

If the popover contains a form, you might need to trap focus within the popover
and close it when the user fills the form and hits "save".

You can leverage
[react-focus-lock](https://github.com/theKashey/react-focus-lock) to trap focus
within the `PopoverContent`.

```jsx manual=true
// import  FocusLock from "react-focus-lock"

// 1. Create a text input component
const TextInput = React.forwardRef((props, ref) => {
  return (
    <FormControl>
      <FormLabel htmlFor={props.id}>{props.label}</FormLabel>
      <Input ref={ref} id={props.id} {...props} />
    </FormControl>
  )
})

// 2. Create the form
const Form = ({ firstFieldRef, onCancel }) => {
  return (
    <Stack spacing={4}>
      <TextInput
        label="First name"
        id="first-name"
        ref={firstFieldRef}
        defaultValue="John"
      />
      <TextInput label="Last name" id="last-name" defaultValue="Smith" />
      <ButtonGroup d="flex" justifyContent="flex-end">
        <Button variant="outline" onClick={onCancel}>
          Cancel
        </Button>
        <Button isDisabled colorScheme="teal">
          Save
        </Button>
      </ButtonGroup>
    </Stack>
  )
}

// 3. Create the Popover
// Ensure you set `closeOnBlur` prop to false so it doesn't close on outside click
const PopoverForm = () => {
  const { onOpen, onClose, isOpen } = useDisclosure()
  const firstFieldRef = React.useRef(null)

  return (
    <>
      <Box d="inline-block" mr={3}>
        John Smith
      </Box>
      <Popover
        isOpen={isOpen}
        initialFocusRef={firstFieldRef}
        onOpen={onOpen}
        onClose={onClose}
        placement="right"
        closeOnBlur={false}
      >
        <PopoverTrigger>
          <IconButton size="sm" icon={<EditIcon />} />
        </PopoverTrigger>
        <PopoverContent p={5}>
          <FocusLock returnFocus persistentFocus={false}>
            <PopoverArrow />
            <PopoverCloseButton />
            <Form firstFieldRef={firstFieldRef} onCancel={onClose} />
          </FocusLock>
        </PopoverContent>
      </Popover>
    </>
  )
}

render(<PopoverForm />)
```

## Controlled Usage

You can control the opening and closing of the popover by passing the `isOpen`,
and `onClose` props.

Sometimes you might need to set the `returnFocusOnClose` prop to `false` to
prevent popver from returning focus to `PopoverTrigger`'s children.

```jsx
function ControlledUsage() {
  const [isOpen, setIsOpen] = React.useState(false)
  const open = () => setIsOpen(!isOpen)
  const close = () => setIsOpen(false)
  return (
    <>
      <Button mr={5} onClick={open}>
        Trigger
      </Button>
      <Popover
        returnFocusOnClose={false}
        isOpen={isOpen}
        onClose={close}
        placement="right"
        closeOnBlur={false}
      >
        <PopoverTrigger>
          <Button colorScheme="pink">Popover Target</Button>
        </PopoverTrigger>
        <PopoverContent>
          <PopoverHeader fontWeight="semibold">Confirmation</PopoverHeader>
          <PopoverArrow />
          <PopoverCloseButton />
          <PopoverBody>
            Are you sure you want to continue with your action?
          </PopoverBody>
          <PopoverFooter d="flex" justifyContent="flex-end">
            <ButtonGroup size="sm">
              <Button variant="outline">Cancel</Button>
              <Button colorScheme="red">Apply</Button>
            </ButtonGroup>
          </PopoverFooter>
        </PopoverContent>
      </Popover>
    </>
  )
}
```

## Accessing Internal state

Chakra provides access to two internal details: `isOpen` and `onClose`. Use the
render prop pattern to gain access to them.

```jsx
function InternalStateEx() {
  const initRef = React.useRef()
  return (
    <Popover closeOnBlur={false} placement="left" initialFocusRef={initRef}>
      {({ isOpen, onClose }) => (
        <>
          <PopoverTrigger>
            <Button>Click to {isOpen ? "close" : "open"}</Button>
          </PopoverTrigger>
          <Portal>
            <PopoverContent>
              <PopoverHeader>This is the header</PopoverHeader>
              <PopoverCloseButton />
              <PopoverBody>
                <Box>
                  Hello. Nice to meet you! This is the body of the popover
                </Box>
                <Button
                  mt={4}
                  colorScheme="blue"
                  onClick={onClose}
                  ref={initRef}
                >
                  Close
                </Button>
              </PopoverBody>
              <PopoverFooter>This is the footer</PopoverFooter>
            </PopoverContent>
          </Portal>
        </>
      )}
    </Popover>
  )
}
```

## Customizing the Popover

Chakra exports all the components you need to customize the look and feel of the
popover. You can change the background, arrow size, box shadow and so on.

```jsx
<Popover>
  <PopoverTrigger>
    <Box
      tabIndex="0"
      role="button"
      aria-label="Some box"
      p={5}
      w="120px"
      bg="gray.300"
      children="Click"
    />
  </PopoverTrigger>
  <PopoverContent bg="tomato" color="white">
    <PopoverHeader fontWeight="semibold">Customization</PopoverHeader>
    <PopoverArrow bg="pink.500" />
    <PopoverCloseButton bg="purple.500" />
    <PopoverBody>
      Tadaa!! The arrow color and background color is customized. Check the
      props for each component.
    </PopoverBody>
  </PopoverContent>
</Popover>
```

## Popover Placements

Since popover is powered by PopperJS, you can change the placement of the
popover by passing the `placement` prop. See the [props](#props) for the
possible placement values.

> Even though you specified the placement, Popover will try to reposition itself
> in the event that available space at the specified placement isn't enough.

```jsx
<Popover placement="top-start">
  <PopoverTrigger>
    <Button>Click me</Button>
  </PopoverTrigger>
  <PopoverContent>
    <PopoverHeader fontWeight="semibold">Popover placement</PopoverHeader>
    <PopoverArrow />
    <PopoverCloseButton />
    <PopoverBody>
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
      tempor incididunt ut labore et dolore.
    </PopoverBody>
  </PopoverContent>
</Popover>
```

### Lazily mounting Popover

By default, the `Popover` component renders children of `PopoverContent` to the
DOM, meaning that invisible popover contents are still rendered but are hidden
by styles.

If you want to defer rendering of popover content until that `Popover` is
opened, you can use the `isLazy` prop. This is useful if your `PopoverContent`
needs to be extra performant, or make network calls on mount that should only
happen when the component is displayed.

```jsx
<Popover isLazy>
  <PopoverTrigger>
    <Button>Click me</Button>
  </PopoverTrigger>
  <PopoverContent>
    <PopoverHeader fontWeight="semibold">Popover placement</PopoverHeader>
    <PopoverArrow />
    <PopoverCloseButton />
    <PopoverBody>
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
      tempor incididunt ut labore et dolore.
    </PopoverBody>
  </PopoverContent>
</Popover>
```

## Accessiblity

> When you see the word _"trigger"_, it is referring to the `children` of
> `PopoverTrigger`

### Keyboard and Focus

- When the popover is opened, focus is moved to the `PopoverContent`. If the
  `initialFocusRef` is set, then focus moves to the element with that `ref`.
- When the popover is closed, focus returns to the trigger. If you set
  `returnFocusOnClose` to `false`, focus will not return.
- If trigger is set to `hover`:
  - Focusing on or mousing over the trigger will open the popover.
  - Blurring or mousing out of the trigger will close the popover. If you move
    your mouse into the `PopoverContent`, it'll remain visible.
- If trigger is set to `click`:
  - Clicking the trigger or using the `Space` or `Enter` when focus is on the
    trigger will open the popover.
  - Clicking the trigger again will close the popover.
- Hitting the `Esc` key while the popover is open and focus is within the
  `PopoverContent`, will close the popover. If you set `closeOnEsc` to `false`,
  it will not close.
- Clicking outside or blurring out of the `PopoverContent` closes the popover.
  If you set `closeOnBlur` to `false`, it will not close.

### ARIA Attributes

- If the trigger is set to `click`, the `PopoverContent` element has role set to
  `dialog`. If the trigger is set to `hover`, the `PopoverContent` has `role`
  set to `tooltip`.
- The `PopoverContent` has `aria-labelledby` set to the `id` of the
  `PopoverHeader`.
- The `PopoverContent` has `aria-describedby` set to the `id` of the
  `PopoverBody`.
- The `PopoverContent` has `aria-hidden` set to `true` or `false` depending on
  the open/closed state of the popover.
- The trigger has `aria-haspopup` set to `true` to denote that it triggers a
  popover.
- The trigger has `aria-controls` set to the `id` of the `PopoverContent` to
  associate the popover and the trigger.
- The trigger has `aria-expanded` set to `true` or `false` depending on the
  open/closed state of the popover.

## Props

### Popover Props

<PropsTable of="Popover" />

### Other Props

- `PopoverContent` composes `Box` and has the ability to smartly position
  itself. Thanks to popper.js.
- `PopoverArrow`, `PopoverHeader`, `PopoverFooter` and `PopoverBody` composes
  `Box`.
- `PopoverCloseButton` composes `Box` component.
